/**
 * Copyright 2007-2008. Chongqing First Information & Network Co., Ltd. All
 * rights reserved. <a>http://www.cqfirst.com.cn</a>
 */
package com.shed.base.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 改自spring security(acegisecurity)
 * 
 * <pre>
 * org.springframework.security.providers.encoding.MessageDigestPasswordEncoder
 * org.apache.commons.codec.binary.Hex
 * </pre>
 * 
 * @author Liuhz create 2007-9-25
 */
public class Md5PasswordEncoder {
	private final static String ALGORITHM = "MD5";

	// ===来自org.springframework.security.providers.encoding.MessageDigestPasswordEncoder===
	/**
	 * Encodes the rawPass using a MessageDigest. If a salt is specified it will
	 * be merged with the password before encoding.
	 * 
	 * @param rawPass
	 *            The plain text password
	 * @param salt
	 *            The salt to sprinkle
	 * @return Hex string of password digest.
	 */
	public static String encodePassword(String rawPass, Object salt) {
		MessageDigest messageDigest = getMessageDigest();

		String saltedPass = mergePasswordAndSalt(rawPass, salt);

		byte[] digest = messageDigest.digest(saltedPass.getBytes());

		return new String(encodeHex(digest));
	}

	/**
	 * Takes a previously encoded password and compares it with a rawpassword
	 * after mixing in the salt and encoding that value
	 * 
	 * @param encPass
	 *            previously encoded password
	 * @param rawPass
	 *            plain text password
	 * @param salt
	 *            salt to mix into password
	 * @return true or false
	 */
	public static boolean isPasswordValid(String encPass, String rawPass, Object salt) {
		String pass1 = "" + encPass;
		String pass2 = encodePassword(rawPass, salt);

		return pass1.equals(pass2);
	}

	/**
	 * Get a MessageDigest instance for the given algorithm. Throws an
	 * IllegalArgumentException if <i>algorithm</i> is unknown
	 * 
	 * @return MessageDigest instance
	 * @throws IllegalArgumentException
	 *             if NoSuchAlgorithmException is thrown
	 */
	private static final MessageDigest getMessageDigest() throws IllegalArgumentException {
		try {
			return MessageDigest.getInstance(ALGORITHM);
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalArgumentException("No such algorithm [" + ALGORITHM + "]");
		}
	}

	/**
	 * Used by subclasses to generate a merged password and salt
	 * <code>String</code>.
	 * <P>
	 * The generated password will be in the form of <code>password{salt}</code>.
	 * </p>
	 * <p>
	 * A <code>null</code> can be passed to either method, and will be handled
	 * correctly. If the <code>salt</code> is <code>null</code> or empty,
	 * the resulting generated password will simply be the passed
	 * <code>password</code>. The <code>toString</code> method of the
	 * <code>salt</code> will be used to represent the salt.
	 * </p>
	 * 
	 * @param password
	 *            the password to be used (can be <code>null</code>)
	 * @param salt
	 *            the salt to be used (can be <code>null</code>)
	 * @return a merged password and salt <code>String</code>
	 * @throws IllegalArgumentException
	 *             if the salt contains '{' or '}' characters.
	 */
	private static String mergePasswordAndSalt(String password, Object salt) {
		if (password == null) {
			password = "";
		}

		if ((salt == null) || "".equals(salt)) {
			return password;
		} else {
			return password + "{" + salt.toString() + "}";
		}
	}

	// =================来自org.apache.commons.codec.binary.Hex=====================
	/**
	 * Used building output as Hex
	 */
	private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
			'f' };

	/**
	 * Converts an array of bytes into an array of characters representing the
	 * hexidecimal values of each byte in order. The returned array will be
	 * double the length of the passed array, as it takes two characters to
	 * represent any given byte.
	 * 
	 * @param data
	 *            a byte[] to convert to Hex characters
	 * @return A char[] containing hexidecimal characters
	 */
	public static char[] encodeHex(byte[] data) {

		int l = data.length;

		char[] out = new char[l << 1];

		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
			out[j++] = DIGITS[0x0F & data[i]];
		}

		return out;
	}
}
